﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Sim3D_practicum.Physics;

namespace Sim3D_practicum.Components
{
    class Ball : DrawableGameComponent, IBallCollision
    {       
        public Vector3 Position { get; set; }
        private Vector3 _velocity;
        public Vector3 Velocity { get { return _velocity; } set { _velocity = value; } }

        private Vector3 _friction;
        public Vector3 Friction { get { return _friction; } set { _friction = value; } }
        
        private Rectangle scaledPosition;//art only
        public const float RADIUS = 28.6f;//radius in mm
        private const float MAXSPEED = 2540;// mm/s

        static Texture2D art;
        private Color colour;
        private int ID;
        private static int availableID = 0;

        public Ball( Vector3 position, Game game )
            : base( game )
        {
            Position = position - new Vector3( RADIUS / 2, 0, RADIUS / 2 );
            scaledPosition = new Rectangle( );
            ID = availableID;
            availableID++;
            colour = new Color( 255 - ID * 35, 255 - ID * 25, 255 - ID * 15 );
        }

        protected override void LoadContent( )
        {
            if ( art == null )
                art = Game.Content.Load<Texture2D>( "Ball" );
            base.LoadContent( );
        }

        public void Draw( GameTime gameTime, SpriteBatch sprite )
        {
            sprite.Begin( );

            float heightScaler = Position.Y;
            if ( heightScaler < 0 )
                heightScaler = 1 / ( 1 - ( heightScaler * 0.002f ) );
            else if ( heightScaler > 0 )
                heightScaler = 1 + heightScaler * 0.002f;
            else
                heightScaler = 1;
            scaledPosition.X = Calculator.mmToPixel( Position.X - RADIUS * heightScaler );
            scaledPosition.Y = Calculator.mmToPixel( Position.Z - RADIUS * heightScaler );
            scaledPosition.Width = Calculator.mmToPixel( RADIUS * 2 * heightScaler );
            scaledPosition.Height = Calculator.mmToPixel( RADIUS * 2 * heightScaler );
            sprite.Draw( art, scaledPosition, colour );

            sprite.End( );
            base.Draw( gameTime );
        }

        public override void Update( GameTime gameTime )
        {
            float t = ( ( float ) gameTime.ElapsedGameTime.Milliseconds / 1000 );
            Position += ( Velocity ) * t;
            Velocity += Pooltable.GRAVITY * t;
            base.Update( gameTime );
        }

        public void CheckForCollision( IBallCollision ball )
        {
            //check what the difference is between this ball and the other ball in positions.
            Vector3 difference = Vector3.Subtract( ball.Position, Position );
            //then we check if we even need to do a check ( if we move away from the other ball, we would just waste performance )
            if ( Vector3.Dot( Velocity, difference ) < 0 )
            {
                //but, since we do not check every ball with every other ball ( if ball 1 and ball 2 had no collision, we are not also going to check
                //ball 2 to ball 1 ). This does  leave the problem of. What is ball 1 is moving towards ball 2, but ball 1 is moving away from ball
                //2? then there would not be a collision test, because the dotProduct would simply be negative. To erase this, we also take the difference
                //of ball 1 to ball 2 and check their dot products
                Vector3 otherDifference = Vector3.Subtract( Position, ball.Position );
                //if the seconds ball isn't moving towards the first ball either, we can safely say that they don't need to check
                if ( Vector3.Dot( ball.Velocity, otherDifference ) < 0 )
                    return;
            }
            //if the distance between the centers of the two balls is smaller than twice the size, we have a collision
            if ( difference.LengthSquared( ) >= RADIUS * RADIUS * 4 )
                return;
            //so we have a collision
            //what we want to do, is create a ray from this ball to the ball we are colliding with. 
            //We do so by taking the position of the other ball, subtract it by our own position, and then multiply it with a factor time. 
            Vector3 parallel = GetParallel( ref ball );
            Vector3 perp = Velocity - parallel;

            CollisionDetected( perp );
            ball.CollisionDetected( ball.Velocity + parallel );
        }
        //we are going to get the purpendicular vector from our position to the position of the ball c. 
        private Vector3 GetParallel( ref IBallCollision ball )
        {
            Vector3 p = Velocity - ball.Velocity;
            Vector3 n = Vector3.Normalize( ball.Position - Position );
            float time = Vector3.Dot( p, n );
            return n * time;
        }
        public void CollisionDetected( Vector3 velocity )
        {            
            Velocity = velocity;
        }
        public Vector3 ActualVelocity
        {
            get { return Velocity - Position; }
        }
        public Vector3 NextPosition
        {
            get { return ActualVelocity + Position; }
        }        
        
        public void print( )
        {
            System.Diagnostics.Debug.WriteLine( ID + ": V " + Velocity + ", P " + Position );
        }

        public void setVelocityX( float v )
        {
            _velocity.X = v;
        }

        public void setVelocityY( float v )
        {
            _velocity.Y = v;
        }

        public void setVelocityZ( float v )
        {
            _velocity.Z = v;
        }
    }
}